package com.gitee.wsl.time

import kotlinx.datetime.UtcOffset
import kotlin.math.absoluteValue
import kotlin.math.sign
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

enum class FixedTimeUnit(val ratioToMillis: Long) {
    MilliSecond(1L),
    Second(1000L),
    Minute(1000L * 60L),
    Hour(1000L * 60L * 60L),
    Day(1000L * 60L * 60L * 24L),
    Week(1000L * 60L * 60L * 24L * 7L)
}

class KZoneOffset(val hours: Int, val minutes: Int) {
    private val ms = hours.sign * (hours.absoluteValue * FixedTimeUnit.Hour.ratioToMillis + minutes * FixedTimeUnit.Minute.ratioToMillis)

    fun toMilliseconds(): Long = ms

    fun toDisplayString(isDisplayZ: Boolean = true): String {
        val s = StringBuilder()
        if (ms == 0L && isDisplayZ) {
            s.append("Z")
        } else {
            val offset = ms.absoluteValue.milliseconds
            s.append(if (ms < 0) '-' else '+')
            s.append(
                "${offset.inWholeHours.toString().padStart(2, '0')}:${
                    offset.inWholeMinutes.toString().padStart(2, '0')
                }"
            )
        }
        return s.toString()
    }

    override fun toString(): String {
        return toDisplayString()
    }

    companion object {
        val UTC = KZoneOffset(0, 0)

        /**
         * @param string format: "+08:00" or "-07:00" or "Z"
         */
        fun parseFrom(string: String): KZoneOffset {
            if (string == "Z" || string == "UTC") {
                return KZoneOffset(0, 0)
            }

            val hour = string.substringBefore(":").let {
                if (it[0] == '+') {
                    it.substring(1)
                } else if (it[0] != '-') {
                    throw IllegalArgumentException("The first character in zone offset should be either '+' or '-'.")
                } else {
                    it
                }
            }.toInt()
            val minute = string.substringAfter(":").toInt()
            return KZoneOffset(hours = hour, minutes = minute)
        }

        /*fun fromMilliseconds(millis: Long): KZoneOffset {
            val sign = if (millis >= 0) 1 else -1
            val duration = Duration.of(millis.absoluteValue, FixedTimeUnit.MilliSecond)
            return KZoneOffset(sign * duration.hourPart(), duration.minutePart())
        }*/

        //fun local(): KZoneOffset = localZoneOffset()
    }
}

fun KZoneOffset.toUtcOffset() = UtcOffset(this.hours,this.minutes)

fun UtcOffset.toDisplayString(isDisplayZ: Boolean = true): String {
    val s = StringBuilder()
    if (totalSeconds == 0 && isDisplayZ) {
        s.append("Z")
    } else {
        val offset = totalSeconds.absoluteValue.seconds
        s.append(if (totalSeconds < 0) '-' else '+')
        s.append(
            "${offset.inWholeHours.toString().padStart(2, '0')}:${
                offset.inWholeMinutes.toString().padStart(2, '0')
            }"
        )
    }
    return s.toString()
}

/*
fun UtcOffset.Companion.parseFrom(string: String): UtcOffset {
    if (string == "Z" || string == "UTC") {
        return UtcOffset.ZERO
    }

    val hour = string.substringBefore(":").let {
        if (it[0] == '+') {
            it.substring(1)
        } else if (it[0] != '-') {
            throw IllegalArgumentException("The first character in zone offset should be either '+' or '-'.")
        } else {
            it
        }
    }.toInt()
    val minute = string.substringAfter(":").toInt()
    return UtcOffset(hours = hour, minutes = minute)
}*/

// TimeZone.Companion.of() should rename parameter 'zoneId' to 'id' as the 'zone' is redundant.
// fun TimeZone.Companion.ofOrNull(id: String): TimeZone? =
//    runCatching { of(id) }.getOrNull()
